package com.example.netty.nio.chatDemo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * NIO 群聊 demo 服务端
 *
 * @author LSQ
 * @since 2023/2/17 20:30
 */
public class ChatServer {

    private static final int PORT = 8888;

    private Selector selector;

    private ServerSocketChannel serverSocketChannel;

    /**
     * 初始化
     *
     * @author LSQ
     * @since 2023/2/18 13:53
     */
    public ChatServer() {
        try {
            // 1、获取通道
            serverSocketChannel = ServerSocketChannel.open();
            // 2、将通道设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 3、绑定链接的端口
            serverSocketChannel.bind(new InetSocketAddress(PORT));
            // 4、选择器Selector
            selector = Selector.open();
            // 5、将通道注册到选择器上
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听
     *
     * @author LSQ
     * @since 2023/2/18 13:57
     */
    public void listen() {
        System.out.println("监听线程：" + Thread.currentThread().getName());
        try {
            // 6、轮询已经就绪的事件
            while (selector.select() > 0) {
                // 7、获取选择器中的所有注册的通道中已经准备就绪的事件
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                // 8、遍历已经准备好的事件
                while (it.hasNext()) {
                    // 9、提取当前事件
                    SelectionKey selectionKey = it.next();
                    // 10、判断当前事件具体是什么
                    if (selectionKey.isAcceptable()) {
                        // 11、获取当前接入的客户端通道
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        // 12、设置为非阻塞模式
                        socketChannel.configureBlocking(false);
                        // 13、将通道注册到selector上
                        System.out.println(socketChannel.getRemoteAddress() + "上线了...");
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } else if (selectionKey.isReadable()) {
                        // 14、处理客户端通道传来的数据
                        this.readData(selectionKey);
                    }
                    // 15、事件处理完毕后移除
                    it.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取客户端传来的数据
     *
     * @param selectionKey selectionKey
     * @author LSQ
     * @since 2023/2/18 14:05
     */
    private void readData(SelectionKey selectionKey) {
        SocketChannel socketChannel = null;
        try {
            // 获取关联的channel
            socketChannel = (SocketChannel) selectionKey.channel();
            // 获取buffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int readCount;
            while ((readCount = socketChannel.read(byteBuffer)) > 0) {
                String msg = new String(byteBuffer.array());
                System.out.println("接收到来自 " + socketChannel.getRemoteAddress() + " 的消息 ---> " + msg);
                // 向其他客户端转发消息
                this.sendToOtherClients(msg, socketChannel);
            }

        } catch (IOException e) {
            try {
                System.out.println(socketChannel.getRemoteAddress() + "离线了...");
//                e.printStackTrace();
                // 取消注册
                selectionKey.cancel();
                // 关闭通道
                socketChannel.close();
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    }

    /**
     * 转发消息到其他客户端
     *
     * @param msg  消息
     * @param self 自身客户端（发送消息的客户端）
     * @author LSQ
     * @since 2023/2/18 14:15
     */
    private void sendToOtherClients(String msg, SocketChannel self) {
        System.out.println("服务器转发消息中...");
        // 遍历所有注册到selector上的channel 并排除自身
        for (SelectionKey selectionKey : selector.keys()) {
            // 通过selectionKey 获取相对应的socketChannel
            Channel targetChannel = selectionKey.channel();
            // 排除自己
            if (targetChannel instanceof SocketChannel && targetChannel != self) {
                // 转型
                SocketChannel socketChannel = (SocketChannel) targetChannel;
                // 将msg缓存到buffer
                ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8));
                // 将buffer写入通道
                try {
                    socketChannel.write(byteBuffer);
                } catch (IOException exception) {
                    exception.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.listen();
    }

}
